﻿

#define GLEW_STATIC

#include <iostream>
#include <string>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "common.h"
#include "Shader.h"
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>


using namespace std;

namespace A0503
{

	// 定义顶点数据组
	float vertices[] = {
		//---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
		0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
		0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
		-0.5f, -0.5f, 0.0f,  0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
		-0.5f,  0.5f, 0.0f,  1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
	};

	// 顶点数据索引目录
	unsigned int indices[] = {
		0,1,2,
		0,2,3
	};

	// 处理窗口输入函数
	void processInput(GLFWwindow* window);

	int execute(TransformCombine combine)
	{
		// 初始化 GLFW 环境
		glfwInit();

		// 提示即将创建窗口时我将使用OpenGL的 主版本号、次版本号及采用哪种配置方式
		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

		// 创建我要使用的窗口
		// 大小：800 X 600 
		// 标题：My OpenGl Learn
		// 
		GLFWwindow* window = glfwCreateWindow(800, 600, "My OpenGl Learn", NULL, NULL);
		if (window == NULL)
		{
			printf("Open Window Failed");
			glfwTerminate();
			return -1;
		}

		// 将之前成功创建出来的window对象作为后续OpenGl整个上下文环境中的操作对象 
		glfwMakeContextCurrent(window);

		// 初始化 GLEW 环境
		glewExperimental = true;
		if (glewInit() != GLEW_OK)
		{
			printf("Init GLEW Failed");
			glfwTerminate();
			return -1;
		}

		// 设置OpenGl绘制图形时,在刚才指定上下文环境window窗口中的视图大小
		glViewport(0, 0, 800, 600);


		// / ===================================   01 创建所需的VAO、VBO、EBO对象并绑定数据  =================================
		// 定义及绑定所需的VAO（顶点数组对象）
		unsigned int VAO;
		glGenVertexArrays(1, &VAO);
		glBindVertexArray(VAO);

		// 定于及绑定所需的VBO（顶点缓存数组）
		unsigned int VBO;
		glGenBuffers(1, &VBO);
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		// 复制顶点数组到缓冲中供OpenGL使用(为绑定的VBO对象灌入我们用户自己定义的顶点数据)
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

		// 定义及绑定EBO（索引缓存对象）
		unsigned int EBO;
		glGenBuffers(1, &EBO);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
		// 复制顶点索引数组到缓存中共OpenGL使用（为绑定的对象灌入我们用户自己定义的顶点索引数据）
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

		// 解析顶点属性：使用0号、1号、2号顶点位置来分为存放取到的坐标信息、颜色信息以及纹理信息（本质就是告诉OpenGl如何解析这个数据）
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
		glEnableVertexAttribArray(1);
		glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
		glEnableVertexAttribArray(2);



		// 创建纹理对象
		unsigned int textureA;
		glGenTextures(1, &textureA);
		unsigned int textureB;
		glGenTextures(1, &textureB);


		// 图形翻转处理
		stbi_set_flip_vertically_on_load(true);

		// 读取纹理图片数据
		glActiveTexture(GL_TEXTURE1);
		glBindTexture(GL_TEXTURE_2D, textureA);
		int width, height, nrChannels;
		unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
		if (data)
		{
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
			glGenerateMipmap(GL_TEXTURE_2D);
		}
		else
		{
			cout << "load file error." << endl;
		}
		stbi_image_free(data);

		glActiveTexture(GL_TEXTURE2);
		glBindTexture(GL_TEXTURE_2D, textureB);
		unsigned char* data2 = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
		if (data2)
		{
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
			glGenerateMipmap(GL_TEXTURE_2D);
		}
		else
		{
			cout << "load file error." << endl;
		}
		stbi_image_free(data2);


		// 使用自定的Shader
		Shader* myShader = new Shader("vs/5.3.vert", "fs/5.3.frag");

		// 使用不同变化顺序来了查看矩阵计算结果(注意书写顺序要反着来)
		glm::mat4 trans;
		switch (combine)
		{
			case TransformCombine::TRS:
				trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
				trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
				trans = glm::translate(trans, glm::vec3(0.5f, 0.5f, 0.0f));
				break;
			case TransformCombine::TSR:
				trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
				trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
				trans = glm::translate(trans, glm::vec3(0.5f, 0.5f, 0.0f));
				break;
			case TransformCombine::RST:
				trans = glm::translate(trans, glm::vec3(0.5f, 0.5f, 0.0f));
				trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
				trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
				break;
			case TransformCombine::RTS:
				trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
				trans = glm::translate(trans, glm::vec3(0.5f, 0.5f, 0.0f));
				trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
				break;
			case TransformCombine::STR:
				trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
				trans = glm::translate(trans, glm::vec3(0.5f, 0.5f, 0.0f));
				trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
				break;
			case TransformCombine::SRT:
				trans = glm::translate(trans, glm::vec3(0.5f, 0.5f, 0.0f));
				trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
				trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
				break;
		}


		// 如果窗口未关闭，则一直进行窗口渲染循环（事件循环）
		while (!glfwWindowShouldClose(window))
		{
			// 处理用户输入
			processInput(window);

			// 渲染之前先清屏处理
			glClearColor(0, 0.5f, 0.5f, 1.0f);
			glClear(GL_COLOR_BUFFER_BIT);

			// 执行渲染指令
			glBindVertexArray(VAO);
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
			myShader->use();

			glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 1);
			glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 2);
			glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "transform"), 1, GL_FALSE, glm::value_ptr(trans));

			glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

			// 检查交换事件&交换缓存
			glfwSwapBuffers(window);
			glfwPollEvents();
		}
		glfwTerminate();

		system("pause");
	}

	void processInput(GLFWwindow* window)
	{
		if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		{
			glfwSetWindowShouldClose(window, true);
		}
	};
}